package com.filldream.sun.openapi;


import cn.hutool.core.codec.Base64;
import com.filldream.sun.bean.result.ErrorCode;
import com.filldream.sun.bean.wxuser.*;
import com.filldream.sun.exception.GlobalException;
import com.filldream.sun.core.*;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.Key;
import java.util.HashMap;
import java.util.Map;

/**
 * 微信授权工具类
 * @author RickSun
 * 2019-10-12
 */
public class WxSnsUtil {

    /**
     * 获取用户信息(用于H5、公众号、APP的微信 unionid机制)
     * @param accessToken
     * @param openId
     * @return 获取用户头像、昵称、性别、国家、openId、unionid(仅添加到开放平台后才有)
     * https://developers.weixin.qq.com/doc/oplatform/Mobile_App/WeChat_Login/Authorized_API_call_UnionID.html
     */
    public static User getUserInfo(String accessToken, String openId){
        String url = "https://api.weixin.qq.com/sns/userinfo";
        Map<String,String> paramMap = new HashMap<>();
        paramMap.put("access_token",accessToken);
        paramMap.put("openid",openId);
        paramMap.put("lang","zh_CN");
        return JsonUtil.fromJson(UrlUtil.doGet(url,paramMap),User.class);
    }


    /**
     * 获取微信H5、公众号、APP、网站的Access_token
     * @param appId
     * @param secret
     * @param code
     * @return
     * {
     * 	"access_token": "ACCESS_TOKEN",
     * 	"expires_in": access_token 接口调用凭证超时时间，单位（秒）,
     * 	"refresh_token": "用户刷新 access_token",
     * 	"openid": "OPENID",
     * 	"scope": "用户授权的作用域，使用逗号（,）分隔",
     * 	"unionid": "当且仅当该移动应用已获得该用户的 userinfo 授权时，才会出现该字段"
     * }
     * https://developers.weixin.qq.com/doc/oplatform/Mobile_App/WeChat_Login/Development_Guide.html
     */
    public static SnsToken getAccessToken(String appId, String secret, String code){
        String url = "https://api.weixin.qq.com/sns/oauth2/access_token";
        Map<String,String> paramMap = new HashMap<>();
        paramMap.put("appid",appId);
        paramMap.put("secret",secret);
        paramMap.put("code",code);
        paramMap.put("grant_type","authorization_code");
        return JsonUtil.fromJson(UrlUtil.doGet(url,paramMap),SnsToken.class);
    }


    /**
     * 获取微信小程序的session_key
     * 登录凭证校验。通过 wx.login 接口获得临时登录凭证 code 后传到开发者服务器调用此接口完成登录流程。
     * @param appId
     * @param secret
     * @param jsCode
     * @return 用户openid、session_key(会话密钥)、unionid
     * https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/login/auth.code2Session.html
     */
    public static Jscode2sessionResult getJs2Seesion(String appId, String secret, String jsCode){
        String url = "https://api.weixin.qq.com/sns/jscode2session";
        Map<String,String> paramMap = new HashMap<>();
        paramMap.put("appid",appId);
        paramMap.put("secret",secret);
        paramMap.put("js_code",jsCode);
        paramMap.put("grant_type","authorization_code");
        return JsonUtil.fromJson(UrlUtil.doGet(url,paramMap),Jscode2sessionResult.class);
    }


    /**
     * 获取微信公众号、小程序的Access_token
     * @param appId
     * @param appSecret
     * @return {"access_token":"ACCESS_TOKEN","expires_in":7200}
     * @throws IOException
     * https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Get_access_token.html
     */
    public static CgiBinToken getAccessToken(String appId,String appSecret) throws IOException {
        String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&"+
                "appid="+ appId +
                "&secret=" + appSecret;
        return JsonUtil.fromJson(UrlUtil.doGet(url,null), CgiBinToken.class);
    }

    /**
     * 获取用户信息(通用小程序和H5)
     * @param code
     * @param encryptedData
     * @param iv
     * @param appId
     * @param secret
     * @return
     * @throws IOException
     */
    public static UserInfo decodeWxUser(String code, String encryptedData, String iv, String appId, String secret) throws IOException {
        UserInfo wxUser = new UserInfo();
        if(SunCommon.isNotBlank(encryptedData)){
            Jscode2sessionResult js2Seesion = getJs2Seesion(appId, secret, code);
            String openid = js2Seesion.getOpenid();
            String unionId = js2Seesion.getUnionid();
            String sessionKey = js2Seesion.getSession_key();
            wxUser.setAppid(appId);
            wxUser.setOpenid(openid);
            //可能为空
            wxUser.setUnionid(unionId);
            wxUser.setSession_key(sessionKey);
            WxaDUserInfo wxaDUserInfo = decryptUserInfo(wxUser.getSession_key(), encryptedData, iv);
            if (null == wxaDUserInfo) {
                throw new GlobalException(ErrorCode.PARAM_ERR.setContent("decodeWxUser failed encryptedData=" + encryptedData + " code=" + code
                    + " iv=" + iv + " credential=" + wxUser.getSession_key()));
            }
            wxUser.setUnionid(wxaDUserInfo.getUnionId());
            wxUser.setCity(wxaDUserInfo.getCity());
            wxUser.setProvince(wxaDUserInfo.getProvince());
            wxUser.setCountry(wxaDUserInfo.getCountry());
            if(wxaDUserInfo.getGender()!=null) {
                wxUser.setSex(Integer.valueOf(wxaDUserInfo.getGender()));
            }
            wxUser.setHeadimgurl(wxaDUserInfo.getAvatarUrl());
            wxUser.setNickname(wxaDUserInfo.getNickName());
            wxUser.setLanguage(wxaDUserInfo.getLanguage());
        }else{
            SnsToken snsToken = getAccessToken(appId, secret, code);
            User userInfo = getUserInfo(snsToken.getAccess_token(), snsToken.getOpenid());
            wxUser.setUnionid(userInfo.getUnionid());
            wxUser.setCity(userInfo.getCity());
            wxUser.setOpenid(snsToken.getOpenid());
            wxUser.setNickname(userInfo.getNickname());
            wxUser.setHeadimgurl(userInfo.getHeadimgurl());
            wxUser.setProvince(userInfo.getProvince());
            wxUser.setCountry(userInfo.getCountry());
            wxUser.setPrivilege(userInfo.getPrivilege());
        }
        return wxUser;
    }

    /**
     * 解密小程序敏感信息
     * @param session_key
     * @param encryptedData
     * @param iv
     * @return
     */
    private static WxaDUserInfo decryptUserInfo(String session_key, String encryptedData, String iv) {
        try {
            Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
            Key sKeySpec = new SecretKeySpec(Base64.decode(session_key), "AES");
            cipher.init(2, sKeySpec, new IvParameterSpec(Base64.decode(iv)));
            byte[] resultByte = cipher.doFinal(Base64.decode(encryptedData));
            String data = new String(PKCS7Util.decode(resultByte), StandardCharsets.UTF_8);
            return JsonUtil.fromJson(data,WxaDUserInfo.class);
        } catch (Exception var7) {
            return null;
        }
    }


}
